#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <conio.h>

const int N = 5;         //存储学生信息的数组的初始大小
const int INCR_SIZE = 1; //每次数组增加的大小

struct StudentInfo
{
    char Class[20];  //班级
    char No[9];      //学号
    char Name[10];   //姓名
    double Score[9]; //分别为该学生9门课的成绩
    double Sum;      //总分
    double Average;  //平均分
    int Rank;        //年级排名
};
struct StudentInfo StudentInto_Start[N] = {{"法律一班", "1201001", "潘志阳", {71.6, 82.8, 76.5, 75.8, 87.9, 76.8, 79.7, 83.9, 88.9}, 728.4, 80.9, 77},
                                           {"法律一班", "1201002", "蒋文奇", {68.5, 88.7, 78.6, 69.6, 93.6, 87.3, 82.5, 81.5, 89.1}, 739.4, 82.2, 64}};

struct StudentInfo *Records; //储存学生信息的结构体数组

char *Subject[9] = {"     英语", "     体育", "   计算机", "  近代史", "   法制史", "  刑法", "  民法", "   法律英语", "  立法法"};

int StudentNum = 2;  //学生记录数
int NUM_SUBJECT = 9; //科目数
int ArraySize = N;   //记录初始长度，并将其设定为N的值
int SavedFlag = 0;   //记录保存次数


void Login();      //登录函数
void ReDisplay();  //按照学号查询成绩，输错学号
void ShowTitle();  //查看表头
void Querybyno();  //按照学号查询成绩
void DisplayAll(); //显示所有学生成绩
void Set();        //选择递归函数
void Menu();       //主菜单
void AddStudent(); //增加学生信息
void Error();      //处理错误
void Sortbyno();   //排序
void DelStudent(); //删除
void DelAll();     //清空结构体
void FinalError(); //最后询问是否保存

int LoadFromFile();        //从文件中读取学生信息
int SaveToFile();          //将学生信息保存到文件
int InitializationFiles(); //初始化Records内存
int SaveToFileStart();     //创建数据库并写入初始数据
int main()
{
    SaveToFileStart();     //数据初始化
    InitializationFiles(); //初始化内存
    Login();               //登录系统
    system("pause");
    return 0;
}
void Menu() //主菜单
{
    system("color A0"); //更改主菜单颜色
    printf("欢迎使用学生信息管理系统！\n");
    printf("\t\t\t\t================================================\n");
    printf("\t\t\t\t| 1:按照学号查询学生信息 | 2:显示所有学生信息  |\n");
    printf("\t\t\t\t| 3:修改或增加学生信息   | 4:删除学生信息      |\n");
    printf("\t\t\t\t| 5:清空所有信息         | 6:把修改保存到数据库|\n");
    printf("\t\t\t\t| 7;从文件中导入信息     | 8:退出系统          |\n");
    printf("\t\t\t\t================================================\n");
    printf("请选择:");
    Set(); //调用功能选择函数
}
void Set() //功能选择函数
{
    int a;
    scanf("%d", &a);
    switch (a)
    {
    case 1:
        system("cls"); //清空屏幕
        fflush(stdin); //清除缓冲区，防止读入回车符
        Querybyno();   //按照学号查询学生信息
        Error();       //返回主菜单
        break;
    case 2:
        system("cls"); //清空屏幕
        Sortbyno();    //排序
        fflush(stdin); //清除缓冲区，防止读入回车符
        DisplayAll();  //显示所有信息
        Error();       //返回主菜单
        break;
    case 3:
        system("cls"); //清空屏幕
        fflush(stdin); //清除缓冲区，防止读入回车符
        AddStudent();  //添加信息
        Error();       //返回主菜单
        break;
    case 4:  
        system("cls"); //清空屏幕
        fflush(stdin); //清除缓冲区，防止读入回车符
        DelStudent();  //删除学生信息
        Error();       //返回主菜单
        break;
    case 5:
        system("cls"); //清空屏幕
        DelAll();      //清空所有信息
        Error();       //返回主菜单
        break;
    case 6:
        system("cls"); //清空屏幕
        SaveToFile();  //保存到文件
        fflush(stdin); //清除缓冲区，防止读入回车符
        Error();       //返回主菜单
        break;
    case 7:
        system("cls");  //清空屏幕
        LoadFromFile(); //从文件中读取信息
        fflush(stdin);  //清除缓冲区，防止读入回车符
        Error();        //返回主菜单
        break;
    case 8:
        FinalError();  //是否保存文件
        free(Records); //释放动态数组内存
        exit(0);       //退出系统
        break;
    default:
        printf("请重新输入有效序号:");
        Set(); //重新加载主菜单
    }
}
void ShowTitle() //显示表头
{
    int j;
    printf("  班级    学号      姓名 ");
    for (j = 0; j < NUM_SUBJECT; j++) //显示科目名称
        printf(" %s", Subject[j]);
    printf("  总分  平均分   年级排名\n");
}

void Querybyno() //根据学号进行查找
{
    char SearChno[9];
    int i, j;
    printf("请输入学号:");
    gets(SearChno);
    for (i = 0; i < StudentNum; i++)
        if (strcmp(SearChno, Records[i].No) == 0)
            break;
    if (i < StudentNum) //找到了该记录
    {
        ShowTitle(); //显示表头信息及记录信息
        printf("%8s  %-8s %2s", Records[i].Class, Records[i].No, Records[i].Name);
        for (j = 0; j < NUM_SUBJECT; j++)
            printf("%9.lf", Records[i].Score[j]);
        printf("%8.lf%8.lf%8.d\n", Records[i].Sum, Records[i].Average,Records[i].Rank);
    }
    else //没有找到记录
        printf("没有学号为%s的学生记录，请重新输入学号", SearChno);
    ReDisplay();
}
void ReDisplay() //使用递归重新加载
{
    char Ok[4];
    printf("\n是否要重新输入学号？(y/n)\n");
    gets(Ok);
    if (Ok[0] == 'y' || Ok[0] == 'Y') //确定要重新输入学号
    {
        Querybyno();
    }
    else if (Ok[0] == 'n' || Ok[0] == 'N')
        printf("\n");
    else
    {
        printf("请输入y/n!\n");
        ReDisplay(); //递归
    }
}
void Sortbyno() // 按学号排序
{
    int i, j;
    StudentInfo temp; //临时存储对象
    // 冒泡排序，升序
    for (i = 1; i <= StudentNum - 1; i++)
    {
        for (j = 0; j <= StudentNum - i - 1; j++)
        {
            if (strcmp(Records[j].No, Records[j + 1].No) > 0)
            {
                temp = Records[j];
                Records[j] = Records[j + 1];
                Records[j + 1] = temp;
            }
        }
    }
    printf("\n排序已经完成\n");
}

void DisplayAll() //显示所有学生成绩
{
    int i, j;
    if (StudentNum == 0) //没有学生信息
        printf("暂时还没有学生信息！\n请按任意键返回主菜单\n");
    else
    {
        ShowTitle(); //显示表头信息
        for (i = 0; i < StudentNum; i++)
        {
            printf("%8s  %-8s %2s", Records[i].Class, Records[i].No, Records[i].Name); //打印学生信息
            for (j = 0; j < NUM_SUBJECT; j++)
                printf("%9.lf", Records[i].Score[j]);
            printf("%8.lf%8.lf%8.d\n", Records[i].Sum, Records[i].Average, Records[i].Rank);
        }
        printf("\n所有学生信息显示完毕，请按任意键继续\n");
    }
    getchar();
}
void AddStudent() //修改或增加学生信息
{
    int i;
    char a[10];
    
     if (StudentNum >= ArraySize)
        {
            Records = (struct StudentInfo *)realloc(Records, (ArraySize + INCR_SIZE) * sizeof(struct StudentInfo));
            if (Records == NULL)
            {
                printf("空间不足，请按任意键返回主菜单\n");
                system("pause");
                Menu();
            }
            ArraySize += INCR_SIZE;
        }
        printf("请输入班级：");
        gets(Records[StudentNum].Class);
        printf("请输入学号：");
        gets(Records[StudentNum].No);
        printf("请输入姓名:");
        gets(Records[StudentNum].Name);

        for (i = 0; i < NUM_SUBJECT; i++)
        {
            printf("请输入%s成绩：(0-100)：", Subject[i]);
            scanf("%lf", &Records[StudentNum].Score[i]);
            if (Records[StudentNum].Score[i] >= 0 && Records[StudentNum].Score[i] <= 100)
                Records[StudentNum].Sum += Records[StudentNum].Score[i];
            else
            {
                printf("输入数据无效，请重新输入……\n");
                fflush(stdin); //清理缓存
                i--;
                continue;
            }
        }
        printf("请输入总分：");
        fflush(stdin);
        gets(a);
        Records[StudentNum].Sum = (double)atof(a);
        printf("请输入平均分：");
        gets(a);
        Records[StudentNum].Average = (double)atof(a);
        printf("请输入年级排名：");
        gets(a);
        Records[StudentNum].Rank = (double)atof(a);
        StudentNum++;
      
}
void Error()
{
    char Ok[4];
    printf("\n您的信息已经保存，是否返回主菜单（y/n）\n");
    gets(Ok);
    if (Ok[0] == 'y' || Ok[0] == 'Y')
    {
        system("cls"); //清空屏幕
        Menu();        //返回功能栏
    }
    else if (Ok[0] == 'n' || Ok[0] == 'N')
    {
        printf("\n");
        free(Records);
    }
    else
    {
        printf("请输入y/n!");
        Error();
    }
}
void FinalError()
{
    char Ok[4];
    printf("\n是否保存到文件（y/n）\n");
    fflush(stdin);
    gets(Ok);
    if (Ok[0] == 'y' || Ok[0] == 'Y') 
    {
        system("cls"); //清空屏幕
        SaveToFile();
        printf("您的信息已经被保存！\n");
    }
    else if (Ok[0] == 'n' || Ok[0] == 'N')
        printf("\n");
    else
    {
        printf("请输入y/n!");
    }
}
void DelStudent() //删除指定记录
{
    char DelNo[9]; //存放要删除学生的学号
    char Ok[1];    //确定是否要真的删除
    int i, j;
    printf("请输入要删除学生的学号：");
    gets(DelNo);
    //根据学号查找记录位置
    for (i = 0; i < StudentNum; i++)
        if (strcmp(DelNo, Records[i].No) == 0)
            break;
    if (i < StudentNum) //找到了该记录
    {
        //找到后先输出原信息，然后确认是否删除
        ShowTitle(); //先显示原信息
        printf("%8s  %-8s %2s", Records[i].Class, Records[i].No, Records[i].Name);
        for (j = 0; j < NUM_SUBJECT; j++)
            printf("%9.lf", Records[i].Score[j]);
        printf("%8.lf%8.lf%8.d\n", Records[i].Sum, Records[i].Average, Records[i].Rank);
        printf("确实要删除这条记录吗？(y/n)\n");
        gets(Ok);
        if (Ok[0] == 'y' || Ok[0] == 'Y') //确定要删除
        {
            StudentNum--; //学生数减1
            //将后面的记录前移
            for (j = i; j < StudentNum; j++)
                Records[j] = Records[j + 1];
            printf("\n删除成功！\n");
            SavedFlag = 1; //修改存储标志
        }
    }
    else
        printf("\n没有该学生记录\n");
    printf("请按任意键继续\n");
    getchar();
}
void DelAll() //清空结构体
{
    char Ok[4];
    printf("\n你真的要清除所有数据吗？(y/n)\n");
    fflush(stdin);
    gets(Ok);
    if (Ok[0] == 'y' || Ok[0] == 'Y') 
    {
        system("cls"); //清空屏幕
        for (int i = 0; i < StudentNum; i++)
        {
            memset(Records + i, 0, sizeof(Records + i)); //清空结构体
        }
        StudentNum = 0;
    }
    else if (Ok[0] == 'n' || Ok[0] == 'N')
        printf("\n");
    else
    {
        printf("请输入y/n!");
    }
}

int SaveToFileStart() //创建数据库并写入初始数据
{
    FILE *fp;
    if ((fp = fopen("DATA.dll", "rt")) == NULL)
    {
        printf("数据库文件不存在！\n将创建新的数据库\n");
        //system("pause");
        if ((fp = fopen("DATA.dll", "wt+")) == NULL)
        {
            printf("失败！\n");
            return 0;
        }
        else
        {
            printf("正在写入初始数据。。。\n");
            fwrite(StudentInto_Start, sizeof(struct StudentInfo) * 2, 1, fp);
            printf("新的数据库创建成功!\n");
            fclose(fp);
            return 1;
        }
        return 0;
    }
    fclose(fp);
    //printf("%d条记录已经存入文件数据库\n", StudentNum);
    system("cls");
    SavedFlag = 0;
    return 1;
}
int SaveToFile() //将学生信息保存到文件
{
    FILE *fp;
    if ((fp = fopen("DATA.dll", "wt")) == NULL)
    {
        printf("文件不存在或文件已损坏！！！\n");

        system("pause");
        return 0;
    }
    printf("正在存储文件。。。");
    fwrite(Records, sizeof(struct StudentInfo) * StudentNum, 1, fp);
    fclose(fp);
    printf("%d条记录已经存入文件数据库\n", StudentNum);
    SavedFlag = 0;
    system("pause");
    return 1;
}

int LoadFromFile() //从文件中读取学生信息
{
    StudentNum = 0;
    FILE *fp;
    if ((fp = fopen("DATA.dll", "rt")) == NULL)
    {
        printf("文件不存在或文件已损坏！！！\n");
        system("pause");
        return 0;
    }
    printf("正在读取数据库...\n");

    while (!feof(fp))
    {
        if (StudentNum >= ArraySize)
        {
            Records = (struct StudentInfo *)realloc(Records, (ArraySize + INCR_SIZE) * sizeof(struct StudentInfo));
            if (Records == NULL)
            { 
                printf("空间不足，请按任意键返回主菜单\n");
                system("pause");
                return 0;
            }
            ArraySize += INCR_SIZE;
        }
        if (fread(&Records[StudentNum], sizeof(struct StudentInfo), 1, fp) != 1)
            break;
        StudentNum++;
    }
    fclose(fp);
    printf("从数据库中提取出%d条学生信息\n", StudentNum);
    system("pause");
    return 1;
}

int InitializationFiles() //初始化Records内存
{
    Records = (struct StudentInfo *)malloc(N * sizeof(struct StudentInfo));
    if (Records == NULL)
    {
        printf("失败\n");
    }
    return 1;
}

void Login()
{
    char name[] = "00000", pwd[] = "admin"; //正确的用户名和密码
    char username[10], userpwd[100];        //用户输入的用户名和密码
    {
        printf("\n\n\n\n\n\n\n\n\n");
        printf("                                             Student achievement inquiry system\n\n");
        system("color F1");
        printf("                                             欢迎使用学生成绩管理系统!\n\n");

    re:
        printf("                                             请输入管理员账号:");
        gets(username);
        printf("\n");
        printf("                                             请输入密码:");
        fflush(stdin); //清理缓存区
        int m;
        for (m = 0; m < 100; m++) //隐藏密码功能
        {
            userpwd[m] = getch(); //读入用户名
            if (userpwd[m] == '\r')
                break;
            if (userpwd[m] == 8 && m > 0) //如果用户按退格键
            {
                printf("\b \b"); //显示退一格
                m = m - 2;       //输入数据退1
                continue;
            }
            if (userpwd[m] == 8 && m == 0) //无格可退时按退格键
            {
                m = m - 1; //输入数据退1
                continue;
            }
            printf("*");
        }
        userpwd[m] = '\0';
        //如果用户名和密码正确,则登录成功,返回1
        printf("\n\n");
        if (strcmp(name, username) == 0 && strcmp(pwd, userpwd) == 0)
        {
            LoadFromFile(); //读取数据
            system("cls");  //清空屏幕
            Menu();         //进入主页面
        }
        else
        {
            printf("                                             账号或密码错误,请重新输入!\n\n");
            goto re; //重新输入账号密码
        }
    }
}
